package com.moyun.syssecurity.sms.filter;

import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.json.JSONUtil;
import com.moyun.common.utils.ValidateUtil;
import com.moyun.common.validation.SMSLoginGroup;
import com.moyun.syssecurity.sms.SmsCodeAuthenticationToken;
import com.moyun.syssecurity.sms.pojo.SMSVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

/**
 * <h3>org</h3>
 * <p>短信登录的鉴权过滤器，模仿 UsernamePasswordAuthenticationFilter 实现</p>
 *
 * @author : ybx
 * @date : 2021-06-15 16:34:53
 **/
@Slf4j
public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    /**
     * form表单中手机号码的字段name
     */
    public static final String SPRING_SECURITY_FORM_MOBILE_KEY = "mobile";

    private String mobileParameter = SPRING_SECURITY_FORM_MOBILE_KEY;
    /**
     * 是否仅 POST 方式
     */
    private boolean postOnly = true;

    public SmsCodeAuthenticationFilter() {
        // 短信登录的请求 post 方式的 /sms/login
        super(new AntPathRequestMatcher("/sms/login", "POST"));
    }


    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
        // 如果不是post请求方式，则返回方法异常
        log.info("进来短信验证码过滤器中");
        if (postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException(
                    "Authentication method not supported: " + request.getMethod());
        }
        // 判断请求体是否传参
        String body = ServletUtil.getBody(request);
        if (StringUtils.isEmpty(body)) {
            throw new AuthenticationServiceException(
                    "Required request body is missing");
        }

        // 从request对象中获取手机号
        SMSVO smsvo = JSONUtil.toBean(body, SMSVO.class);
        // 利用 @NotEmpty 注解进行参数校验
        List<String> valid = ValidateUtil.validate(smsvo, SMSLoginGroup.class);

        if (!CollectionUtils.isEmpty(valid)) {
            throw new AuthenticationServiceException(
                    String.join(",", valid));
        }
        // 将手机号和短信验证码 封装到  SmsCodeAuthenticationToken 对象中交由 SpringSecurity去认证
        SmsCodeAuthenticationToken authRequest = new SmsCodeAuthenticationToken(smsvo.getPhone(), smsvo.getSmsCode());

        // Allow subclasses to set the "details" property
        setDetails(request, authRequest);

        return this.getAuthenticationManager().authenticate(authRequest);
    }

    protected void setDetails(HttpServletRequest request, SmsCodeAuthenticationToken authRequest) {
        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
    }

    public String getMobileParameter() {
        return mobileParameter;
    }

    public void setMobileParameter(String mobileParameter) {
        Assert.hasText(mobileParameter, "Mobile parameter must not be empty or null");
        this.mobileParameter = mobileParameter;
    }

    public void setPostOnly(boolean postOnly) {
        this.postOnly = postOnly;
    }
}